﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace FloorDesigner
{
    public enum DesignMode { Moving, DesignNew }

    public static class TypeExtensions
    {
        public static IList<Type> GetAllBaseTypes(this Type type)
        {
            var t = type.BaseType;
            var typeList = new List<Type>();
            while (t != null)
            {
                typeList.Add(t);
                t = t.BaseType;
            }

            return typeList;
        }

        public static bool InheritsType(this Type type, Type baseTypeToCheck)
        {
            if (type.GetAllBaseTypes().Contains(baseTypeToCheck))
                return true;
            else return false;
        }
    }

    internal struct Vector2D
    {
        internal Vector2D(double x, double y) : this()
        {
            this.X = x;
            this.Y = y;
        }

        internal double X { get; set; }
        internal double Y { get; set; }
    }

    internal static class SilverlightExtensions
    {
        internal static Vector2D GetTotalScales(this Transform source, Vector2D scale)
        {
            double x = scale.X; double y = scale.Y;

            if (source.GetType() == typeof(ScaleTransform))
            {
                var s = (ScaleTransform)source;
                x *= s.ScaleX;
                y *= s.ScaleY;
            }
            else if (source.GetType() == typeof(TransformGroup))
            {
                foreach (var child in ((TransformGroup)source).Children)
                {
                    if (child.GetType() == typeof(ScaleTransform))
                    {
                        var resVector = child.GetTotalScales(new Vector2D(1.0, 1.0));
                        x *= resVector.X; y *= resVector.Y;
                    }
                }
            }

            return new Vector2D(x, y);
        }

        internal static Transform ExcludeTransform(this Transform source, Type transformType)
        {
            Transform copy = null;

            if (source.GetType() == typeof(TransformGroup))
            {
                copy = new TransformGroup();
                var coll = from c in ((TransformGroup)source).Children
                           where c.GetType() != transformType
                           select c;
                foreach (var c in coll)
                    ((TransformGroup)copy).Children.Add(c);
            }
            else
            {
                if (source.GetType() != transformType)
                    copy = source;
            }

            // no ScaleTransform found
            return copy;
        }

        internal static bool SelfOrParentVisualsImplementInterface<T>(
           DependencyObject objectToCheck,
           Type typeOfInterface,
           out T implementedInterface) where T : class
        {
            var p = objectToCheck;
            while (p != null)
            {
                if (p.GetType().GetInterfaces().Contains(typeOfInterface))
                {
                    implementedInterface = p as T;
                    return true;
                }

                p = VisualTreeHelper.GetParent(p);
            }

            implementedInterface = null;
            return false;
        }

        internal static Panel GetHighestContainerTarget(Collection<Panel> dropTargets)
        {
            Panel dropTarget = null;
            if (dropTargets != null && dropTargets.Count > 0)
                dropTarget = dropTargets[0];
            return dropTarget;
        }

        internal static Collection<Panel> GetSortedContainersByAbsoluteMousePos(
            Point absolutePosition,
            IEnumerable<Panel> allContainers)
        {
            if (allContainers == null) return null;

            var hitPos = absolutePosition;
            var possibleTargets =
                (List<UIElement>)VisualTreeHelper.FindElementsInHostCoordinates(
                    hitPos,
                    Application.Current.RootVisual);

            var targetList = from t in possibleTargets
                             where allContainers.Contains(t as Panel)
                             orderby t.GetValue(Canvas.ZIndexProperty) descending
                             select t as Panel;

            return new Collection<Panel>(targetList.ToList());
        }

        internal static Panel GetFirstParentPanel(DependencyObject child)
        {
            DependencyObject p = (DependencyObject)child;
            Panel lastPanel = null;
            do
            {
                p = VisualTreeHelper.GetParent(p);
                if (p != null &&
                    p.GetType().InheritsType(typeof(Panel)))
                    lastPanel = (Panel)p;
            } while (p != null);

            return lastPanel;
        }

        internal static int GetZIndexAndSetMaxZIndex(UIElement element)
        {
            var initialZIndex = Convert.ToInt32(element.GetValue(Canvas.ZIndexProperty), CultureInfo.CurrentCulture);
            Canvas.SetZIndex(element, short.MaxValue - 1);

            return initialZIndex;
        }

        internal static void RemoveElementFromParentPanel(FrameworkElement removeObject)
        {
            ((Panel)removeObject.Parent).Children.Remove(removeObject);
        }

    }

    public static class DependencyPropertyCopier
    {
        public static T Copy<T>(this T source) where T : DependencyObject
        {
            T objCopy = (T)Activator.CreateInstance(source.GetType());

            CopyAllDependencyProperties<T>(source, objCopy);

            CopyAllProperties<T>(source, objCopy);

            return objCopy;
        }

        private static void CopyAllProperties<T>(T source, T objCopy) where T : DependencyObject
        {
            Type t = source.GetType();
            PropertyInfo[] pis = t.GetProperties();
            for (int i = 0; i < pis.Length; i++)
            {

                if (pis[i].Name != "Name" &&
                    pis[i].Name != "Parent" &&
                    pis[i].CanRead && pis[i].CanWrite &&
                    !pis[i].PropertyType.IsArray &&
                    pis[i].GetIndexParameters().Length == 0 &&
                    pis[i].GetValue(source, null) != null)
                {
                    try
                    {
                        pis[i].SetValue(objCopy, pis[i].GetValue(source, null), null);
                    }
                    catch { }
                }
                else if (pis[i].PropertyType.GetInterface("IList", true) != null)
                {
                    int cnt = (int)pis[i].PropertyType.InvokeMember("get_Count", BindingFlags.InvokeMethod, null, pis[i].GetValue(source, null), null);
                    for (int c = 0; c < cnt; c++)
                    {
                        object val = pis[i].PropertyType.InvokeMember("get_Item", BindingFlags.InvokeMethod, null, pis[i].GetValue(source, null), new object[] { c });

                        object nVal = val;
                        DependencyObject v = val as DependencyObject;
                        if (v != null)
                            nVal = v.Copy();

                        try
                        {
                            pis[i].PropertyType.InvokeMember("Add", BindingFlags.InvokeMethod, null, pis[i].GetValue(objCopy, null), new object[] { nVal });
                        }
                        catch { }
                    }
                }
            }
        }

        private static void CopyAllDependencyProperties<T>(T source, T objCopy) where T : DependencyObject
        {
            Type t = source.GetType();
            Type wt = t;
            while (wt.BaseType != typeof(DependencyObject))
            {
                FieldInfo[] fi = wt.GetFields(BindingFlags.Static | BindingFlags.Public);
                for (int i = 0; i < fi.Length; i++)
                {
                    {
                        DependencyProperty dp = fi[i].GetValue(source) as DependencyProperty;
                        if (dp != null && fi[i].Name != "NameProperty")
                        {
                            DependencyObject obj = source.GetValue(dp) as DependencyObject;
                            if (obj != null)
                            {
                                object o = obj.Copy();
                                objCopy.SetValue(dp, o);
                            }
                            else
                            {
                                if (fi[i].Name != "CountProperty" &&
                                    fi[i].Name != "GeometryTransformProperty" &&
                                    fi[i].Name != "ActualWidthProperty" &&
                                    fi[i].Name != "ActualHeightProperty" &&
                                    fi[i].Name != "MaxWidthProperty" &&
                                    fi[i].Name != "MaxHeightProperty" &&
                                    fi[i].Name != "StyleProperty" &&
                                    fi[i].Name != "TemplateProperty")
                                {
                                    try
                                    {
                                        objCopy.SetValue(dp, source.GetValue(dp));
                                    }
                                    catch (InvalidOperationException)
                                    {
                                        throw;
                                    }
                                }

                            }
                        }
                    }
                }
                wt = wt.BaseType;
            }
        }
    }

    static internal class ZoomAbleContainerExtensions
    {
        internal static double GetDiffZoomFactorBy2Panels(Panel parentPanel, Panel targetPanel)
        {
            return GetDiffZoomFactorBy2Panels(parentPanel as IZoomAbleContainer, targetPanel as IZoomAbleContainer);
        }

        internal static double GetDiffZoomFactorBy2Panels(IZoomAbleContainer parentPanel, IZoomAbleContainer targetPanel)
        {
            var currentContainerParentZoomFactor = 1.0;
            var dropTargetZoomFactor = 1.0;

            if (parentPanel != null)
                currentContainerParentZoomFactor = ((IZoomAbleContainer)parentPanel).ZoomFactor;
            if (targetPanel != null)
                dropTargetZoomFactor = ((IZoomAbleContainer)targetPanel).ZoomFactor;
            var diffZoomFactor = dropTargetZoomFactor / currentContainerParentZoomFactor;
            return diffZoomFactor;
        }
    }
}
